# _*_ coding: utf-8 _*_
# .@FileName:test
# .@Data....:2025-04-21 : 16 : 31
# .@Aurhor..:LiuJingYu
"""
launch:
from __future__ import unicode_literals, print_function
import maya.cmds as mc
"""
import maya.cmds as cmds


def avg_joint(drivers, weight=0.5):
    """
    create average joint between drivers

    Args:
        drivers (list): driver joints, the average joint will be parented to the first driver
        weight (float): weight blend between drivers

    Returns:
        avg_jnt(str): average joint's name
    """
    # get name parts
    name_parts = drivers[0].split('_')
    # create average joint
    avg_jnt = cmds.createNode('joint', name=drivers[0].replace(name_parts[2], name_parts[2] + 'Avg'))
    # snap to driver's position
    cmds.matchTransform(avg_jnt, drivers[0], position=True)
    # do a temp orient constraint to get average orientation
    cons_node = cmds.orientConstraint(drivers, avg_jnt, maintainOffset=False)[0]
    cmds.setAttr(cons_node + '.interpType', 2)
    cmds.setAttr('{}.{}W0'.format(cons_node, drivers[0]), weight)
    cmds.setAttr('{}.{}W1'.format(cons_node, drivers[1]), 1 - weight)
    # delete orient constraint node
    cmds.delete(cons_node)
    # freeze transformation to bake the orientation to joint orient
    cmds.makeIdentity(avg_jnt, apply=True, translate=True, rotate=True, scale=True)
    # parent to driver node
    cmds.parent(avg_jnt, drivers[0])

    # do orient constraint to drive the avg joint
    cons_node = cmds.orientConstraint(drivers, avg_jnt, maintainOffset=False)[0]
    cmds.setAttr(cons_node + '.interpType', 2)
    cmds.setAttr('{}.{}W0'.format(cons_node, drivers[0]), weight)
    cmds.setAttr('{}.{}W1'.format(cons_node, drivers[1]), 1 - weight)

    # return the average joint name
    return avg_jnt


def add_push_joint(avg_jnt, driver_axis='z', distance_axis='y', scale_axis='x', driver_value=60, distance_value=5,
                   scale_value=0.2):
    """
    add push joint to existing average joint

    Args:
        avg_jnt (str): average joint's name
        driver_axis (str): driver rotation axis
        distance_axis (str): pushing axis
        scale_axis (str): scale axis
        driver_value (float): driver rotation maximum value
        distance_value (float): maximum pushing value
        scale_value (float): maximum scaling value

    Returns:
        push_jnt (str): push joint's name
    """
    # get name parts
    name_parts = avg_jnt.split('_')
    name_side = name_parts[1]
    name_description = name_parts[2] + 'Push'
    name_index = int(name_parts[3])

    # create push joint
    push_jnt = cmds.createNode('joint', name='jnt_{}_{}_{:03d}'.format(name_side, name_description, name_index))
    # snap to average joint position and freeze transformation
    cmds.matchTransform(push_jnt, avg_jnt, position=True, rotation=True)
    cmds.makeIdentity(push_jnt, apply=True, translate=True, rotate=True, scale=True)
    cmds.parent(push_jnt, avg_jnt)

    # add remap value node to drive the push distance
    remap_dis = cmds.createNode('remapValue', name='remap_{}_{}Dis_{:03d}'.format(name_side, name_description,
                                                                                  name_index))
    # connect driver attr to remap node and set range
    cmds.connectAttr('{}.rotate{}'.format(avg_jnt, driver_axis.upper()), remap_dis + '.inputValue')
    cmds.setAttr(remap_dis + '.inputMin', 0)
    cmds.setAttr(remap_dis + '.inputMax', driver_value)
    cmds.setAttr(remap_dis + '.outputMin', 0)
    cmds.setAttr(remap_dis + '.outputMax', distance_value)
    cmds.connectAttr(remap_dis + '.outValue', '{}.translate{}'.format(push_jnt, distance_axis.upper()))

    # add remap value node to drive the scale
    remap_scl = cmds.createNode('remapValue', name='remap_{}_{}Scl_{:03d}'.format(name_side, name_description,
                                                                                  name_index))
    # connect driver attr to remap node and set range
    cmds.connectAttr('{}.rotate{}'.format(avg_jnt, driver_axis.upper()), remap_scl + '.inputValue')
    cmds.setAttr(remap_scl + '.inputMin', 0)
    cmds.setAttr(remap_scl + '.inputMax', driver_value)
    cmds.setAttr(remap_scl + '.outputMin', 1)
    cmds.setAttr(remap_scl + '.outputMax', scale_value)
    cmds.connectAttr(remap_scl + '.outValue', '{}.scale{}'.format(push_jnt, scale_axis.upper()))

    # return push joint's name
    return push_jnt


avg_jnt_info = {}

for finger in ['middle']:
    for digit in ['Base', 'Mid', 'Tip']:
        jnt = 'jnt_l_{}{}_001'.format(finger, digit.title())
        if cmds.objExists(jnt):
            parent_jnt = cmds.listRelatives(jnt, parent=True)[0]
            avg_jnt_info.update({jnt: {'driver': parent_jnt,
                                       'weight': 0.5,
                                       'driver_axis': 'z',
                                       'distance_axis': 'y',
                                       'scale_axis': 'x',
                                       'driver_value': 45,
                                       'distance_value': 1.5,
                                       'scale_value': 0.2}})

for jnt, jnt_info in avg_jnt_info.items():
    avg_jnt_left = avg_joint([jnt, jnt_info['driver']], weight=jnt_info['weight'])
    avg_jnt_right = avg_joint([jnt.replace('_l_', '_r_'), jnt_info['driver'].replace('_l_', '_r_')],
                              weight=jnt_info['weight'])

    push = jnt_info.get('push', True)
    if push:
        add_push_joint(avg_jnt_left, driver_axis=jnt_info['driver_axis'], distance_axis=jnt_info['distance_axis'],
                       scale_axis=jnt_info['scale_axis'], driver_value=jnt_info['driver_value'],
                       distance_value=jnt_info['distance_value'], scale_value=jnt_info['scale_value'])

        add_push_joint(avg_jnt_right, driver_axis=jnt_info['driver_axis'], distance_axis=jnt_info['distance_axis'],
                       scale_axis=jnt_info['scale_axis'], driver_value=jnt_info['driver_value'],
                       distance_value=-jnt_info['distance_value'], scale_value=jnt_info['scale_value'])
